//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <elasysapi.h>
#include <winhack.h>
#include "mouse.h"
#include "data.h"
#include "notifiercenter.h"

static CMouse * g_pMouse = NULL;

static TOUCH_DATA TouchInfo = {0, 0, 0};
static BOOL bPenPush = FALSE;

UINT g_PenTimerID = 0;
EXTERN_C UINT _StartTimer(UINT uSeconds, void * pTimerProc, UINT uType);
EXTERN_C void _StopTimer(UINT wTimerID);
__inline void StopPenTimer()
{
    if (g_PenTimerID) {
        _StopTimer(g_PenTimerID);
        g_PenTimerID = 0;
    }
}

void __stdcall OnMouseDownTimerOut(UINT wTimerID, UINT msg,
    DWORD dwUser, DWORD dw1, DWORD dw2)
{
    StopPenTimer();

    if (TouchInfo.dir == 1/*down*/) {
        //Insert pen push event
        TouchInfo.dir = 3/*push*/;
        bPenPush = TRUE;
        //printf("Touch %d: (%d, %d)\n", TouchInfo.dir, TouchInfo.x, TouchInfo.y);
        MemoryBuf_<sizeof(TOUCH_DATA)> buf;
        buf.Copy((Byte *)&TouchInfo, sizeof(TOUCH_DATA));
        g_pMouse->GetQueue()->Write(0, buf, NULL, NULL);
    }
}

__inline void StartPenTimer(UINT uSec)
{
    if (g_PenTimerID) StopPenTimer();
    g_PenTimerID = _StartTimer(uSec, (void *)OnMouseDownTimerOut, 0);
}


void TouchNotify(int dir, int x, int y)
{
    //int ox = x, oy = y;
    if (dir == 1/*down*/) {
        StartPenTimer(1/*seconds*/);
    }
    else {
        StopPenTimer();
    }

    int X = x, Y = y;
    TouchInfo.dir = (short)dir;
    TouchInfo.x = (short)X;
    TouchInfo.y = (short)Y;

    //Check if pen push before
    if (dir == 1/*down*/ || dir == 0/*up*/) {
        if (dir == 0/*up*/ && bPenPush == TRUE) TouchInfo.dir = 8/*push_up*/;
        bPenPush = FALSE;
    }

    //if (TouchInfo.dir == 8/*push_up*/) {
    //    printf("Touch %d: (%d, %d)\n", TouchInfo.dir, TouchInfo.x, TouchInfo.y);
    //}

    MemoryBuf_<sizeof(TOUCH_DATA)> buf;
    buf.Copy((Byte *)&TouchInfo, sizeof(TOUCH_DATA));

    g_pMouse->GetQueue()->Write(0, buf, NULL, NULL);
}

static void _Mouse_MainService(int id, int code, void * params)
{
    MouseParams * pParams    = (MouseParams *)params;
    //CMouse      * pMouse     = pParams->pMouse;
    // SDL_Event   * pSDLEvent  = (SDL_Event *)pParams->pSDLEvent;
    TOUCH_DATA  * pTouchData = (TOUCH_DATA *)pParams->pTouchData;
    TouchNotify(pTouchData->dir,pTouchData->x,pTouchData->y);
}

static void _Mouse_EnqueueEventService(int id, int code, void * params)
{
    assert(NULL != g_pMouse);

    MemoryBuf_<sizeof(TOUCH_DATA)> buf;
    buf.Copy((Byte *)params, sizeof(TOUCH_DATA));

    g_pMouse->GetQueue()->Write(0, buf, NULL, NULL);
}

CMouse::~CMouse()
{
    if (NULL != m_pEventQueue) {
        m_pEventQueue->Release();
        m_pEventQueue = NULL;
    }
    RemoveNotifier(EVENT_TOUCH);
    RemoveNotifier(SERVICE_MOUSE);
}

ECode CMouse::Initialize()
{
    AddNotifier(SERVICE_MOUSE, (const PVoid)_Mouse_MainService);
    AddNotifier(EVENT_TOUCH, (const PVoid)_Mouse_EnqueueEventService);
    return GetEventQueueSink()->CreateQueue(
                                    m_environmentId,
                                    HOST_MOUSE,
                                    sizeof(TOUCH_DATA), // todo;
                                    32,
                                    &m_pEventQueue);
}

ECode CMouse::Read(
    /* [in] */ Int64 offset,
    /* [in] */ Int32 numberOfBytesToRead,
    /* [out] */ MemoryBuf * pData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return m_pEventQueue->Read(offset,
                       numberOfBytesToRead,
                       pData,
                       ppCompletionEvent);
}

ECode CMouse::Write(
    /* [in] */ Int64 offset,
    /* [in] */ const MemoryBuf & ebbData,
    /* [out] */ Int32 * pNumberOfBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    // TODO;
    return E_NOT_IMPLEMENTED;
}

ECode CMouse::Control(
    /* [in] */ Handle32 controlCode,
    /* [in] */ const MemoryBuf & ebbInData,
    /* [out] */ MemoryBuf * pOutData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void CMouse::Dispose()
{
    delete this;
}

ECode CMouse::S_Create(
        /* [in] */ Int32 environmentId,
        /* [out] */ IDeviceDriver ** ppMouse)
{
    CMouse * pMouse = NULL;
    ECode ec = NOERROR;

    pMouse = new CMouse(environmentId);
    if (NULL == pMouse) {
        return E_OUT_OF_MEMORY;
    }
    ec = pMouse->Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pMouse->AddRef();

    g_pMouse = pMouse;
    *ppMouse = (IDeviceDriver *)pMouse;
    return ec;

ErrorExit:
    if (NULL != g_pMouse) {
        delete g_pMouse;
        g_pMouse = NULL;
    }
    return ec;
}

CMouse *  CMouse::S_Get(
        /* [in] */ Int32 environmentId)
{
    // assert(NULL != g_pMouse);
    return g_pMouse;
}

